﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using uTrade.Common;

namespace uTrade.Core
{
    /// <summary>
    ///
    /// </summary>
    public class TransDatas : Collection<Bar>
    {
        internal OrderItem LastOrder
        {
            get
            {
                return Operations.Count > 0 ? Operations.Last() : new OrderItem();
            }
        }

        public TransDatas()
        {
            CurrentMinBar = new Bar();

            DateTimeUTC = new DataSeries();
            //Time = new DataSeries();
            Open = new DataSeries();
            Close = new DataSeries();
            High = new DataSeries();
            Low = new DataSeries();
            Volume = new DataSeries();
            OpenInterest = new DataSeries();
        }

        private CollectionChange _onChange;

        /// <summary>
        /// 策略变化:加1;减-1;更新0
        /// </summary>
        public event CollectionChange OnChanged
        {
            add
            {
                _onChange += value;
            }
            remove
            {
                _onChange -= value;
            }
        }

        #region 数据序列

        /// <summary>
        /// 日期(yyyyMMdd)
        /// </summary>
        public DataSeries DateTimeUTC { get; private set; }

        /// <summary>
        /// 时间(0.HHmmss)
        /// </summary>
        //public DataSeries Time { get; private set; }

        /// <summary>
        /// 开盘价
        /// </summary>
        public DataSeries Open { get; private set; }

        /// <summary>
        /// 最高价
        /// </summary>
        public DataSeries High { get; private set; }

        /// <summary>
        /// 最低价
        /// </summary>
        public DataSeries Low { get; private set; }

        /// <summary>
        /// 收盘价
        /// </summary>
        public DataSeries Close { get; private set; }

        /// <summary>
        /// 成交量
        /// </summary>
        public DataSeries Volume { get; private set; }

        /// <summary>
        /// 持仓量
        /// </summary>
        public DataSeries OpenInterest { get; private set; }

        #endregion 数据序列

        #region Properties futures

        /// <summary>
        /// 合约信息
        /// </summary>
        [Description("合约信息"), Category("数据"), Browsable(false)]
        public InstrumentInfo InstrumentInfo { get; set; }

        /// <summary>
        /// 合约
        /// </summary>
        [Description("合约"), Category("配置")]
        public string Instrument { get; set; } = string.Empty;

        /// <summary>
        /// 委托合约
        /// </summary>
        [Description("委托合约"), Category("配置")]
        public string InstrumentOrder { get; set; } = string.Empty;

        #endregion Properties futures

        #region Properties stock

        /// <summary>
        /// 股票代码
        /// </summary>
        [Description("股票代码"), Category("配置")]
        public string Symbol { get; set; } = string.Empty;

        /// <summary>
        /// 股票信息
        /// </summary>
        [Description("股票信息"), Category("数据"), Browsable(false)]
        public StockInfo StockInfo { get; set; }

        #endregion Properties stock

        /// <summary>
        /// 实际行情(无数据时为Instrument== null)
        /// </summary>
        [Description("分笔数据"), Category("数据"), Browsable(false)]
        public Tick Tick { get; set; } = new Tick();

        /// 周期类型
        /// </summary>
        [Description("周期类型"), Category("配置")]
        public IntervalType IntervalType { get; set; } = IntervalType.Min;

        /// <summary>
        /// 周期数
        /// </summary>
        [Description("周期数"), Category("配置")]
        public int Interval { get; set; } = 5;

        /// <summary>
        /// 当前K线索引(由左向右从0开始)
        /// </summary>
        [Description("当前K线索引"), Category("设计"), Browsable(false)]
        public int CurrentBar { get => Count == 0 ? -1 : (Count - 1); }

        /// <summary>
        /// 当前的1分钟K线
        /// </summary>
        public Bar CurrentMinBar { get; private set; }

        /// <summary>
        /// 被tick行情调用
        /// </summary>
        /// <param name="f"></param>
        /// <exception cref="Exception"></exception>
        public void OnTick(Tick f)
        {
            #region 生成or更新K线

            DateTime dt = DateTime.ParseExact(f.UpdateTime, "yyyyMMdd HH:mm:ss", null);
            DateTime dtBegin = dt.Date;
            switch (IntervalType)
            {
                case IntervalType.Sec:
                    dtBegin = dtBegin.Date.AddHours(dt.Hour).AddMinutes(dt.Minute).AddSeconds(dt.Second / Interval * Interval);
                    break;

                case IntervalType.Min:
                    dtBegin = dtBegin.Date.AddHours(dt.Hour).AddMinutes(dt.Minute / Interval * Interval);
                    break;

                case IntervalType.Hour:
                    dtBegin = dtBegin.Date.AddHours(dt.Hour / Interval * Interval);
                    break;

                case IntervalType.Day:
                    dtBegin = dt.Date;
                    break;

                case IntervalType.Week:
                    //dtBegin = DateTime.ParseExact(f.TradingDay.ToString(), "yyyyMMdd", null);
                    dtBegin = dtBegin.Date.AddDays(1 - (byte)dtBegin.DayOfWeek);
                    break;

                case IntervalType.Month:
                    //dtBegin = DateTime.ParseExact(f.TradingDay.ToString(), "yyyyMMdd", null);
                    dtBegin = new DateTime(dtBegin.Year, dtBegin.Month, 1);
                    break;

                case IntervalType.Year:
                    //dtBegin = DateTime.ParseExact(f.TradingDay.ToString(), "yyyyMMdd", null);
                    dtBegin = new DateTime(dtBegin.Year, 1, 1);
                    break;

                default:
                    throw new Exception("参数错误");
            }
            if (CurrentMinBar == null)
            {
                CurrentMinBar = new Bar
                {
                    DateTime = DateTime.ParseExact(f.UpdateTime.Substring(0, f.UpdateTime.Length - 3), "yyyyMMdd HH:mm", null),
                    //TradingDay = f.TradingDay,
                    PreVol = f.Volume,
                    OpenInterest = f.OpenInterest,
                    Volume = 0
                };
                CurrentMinBar.High = CurrentMinBar.Low = CurrentMinBar.Open = CurrentMinBar.Close = f.LastPrice;
            }
            else
            {
                if (CurrentMinBar.DateTime - dt < TimeSpan.FromMinutes(1))
                {
                    CurrentMinBar.High = Math.Max(CurrentMinBar.High, f.LastPrice);
                    CurrentMinBar.Low = Math.Min(CurrentMinBar.Low, f.LastPrice);
                    CurrentMinBar.Close = f.LastPrice;
                    CurrentMinBar.Volume = CurrentMinBar.Volume + f.Volume - CurrentMinBar.PreVol;
                    CurrentMinBar.PreVol = f.Volume;       //逐个tick累加
                    CurrentMinBar.OpenInterest = f.OpenInterest;
                }
                else
                {
                    CurrentMinBar.DateTime = DateTime.ParseExact(f.UpdateTime.Substring(0, f.UpdateTime.Length - 3), "yyyyMMdd HH:mm", null);
                    //CurrentMinBar.TradingDay = f.TradingDay;
                    CurrentMinBar.OpenInterest = f.OpenInterest;
                    CurrentMinBar.Volume = f.Volume - CurrentMinBar.PreVol;
                    CurrentMinBar.PreVol = f.Volume;
                    CurrentMinBar.High = CurrentMinBar.Low = CurrentMinBar.Open = CurrentMinBar.Close = f.LastPrice;
                }
            }
            if (Count == 0) //无数据
            {
                Bar bar = new Bar
                {
                    DateTime = dtBegin,
                    //TradingDay = f.TradingDay,
                    PreVol = f.Volume,
                    Volume = 0, // kOld.preVol == 0 ? 0 : _tick.Volume - kOld.preVol;
                    OpenInterest = f.OpenInterest
                };
                bar.High = bar.Low = bar.Open = bar.Close = f.LastPrice;
                Add(bar);
            }
            else
            {
                Bar bar = this[CurrentBar];
                if (bar.DateTime == dtBegin) //在当前K线范围内
                {
                    bar.High = Math.Max(bar.High, f.LastPrice);
                    bar.Low = Math.Min(bar.Low, f.LastPrice);
                    bar.Close = f.LastPrice;
                    bar.Volume = bar.Volume + f.Volume - bar.PreVol;
                    bar.PreVol = f.Volume;      //逐个tick累加
                    bar.OpenInterest = f.OpenInterest;
                    this[CurrentBar] = bar; //更新会与 _onChange?.Invoke(0, old, item); 连动
                }
                else if (dtBegin > bar.DateTime)
                {
                    Bar di = new Bar
                    {
                        DateTime = dtBegin,
                        //TradingDay = f.TradingDay,
                        //V = Math.Abs(bar.PreVol - 0) < 1E-06 ? 0 : f.Volume - bar.PreVol,
                        Volume = f.Volume - bar.PreVol,
                        PreVol = f.Volume,
                        OpenInterest = f.OpenInterest,
                        Open = f.LastPrice,
                        High = f.LastPrice,
                        Low = f.LastPrice,
                        Close = f.LastPrice
                    };
                    Add(di);
                }
            }
            Tick = f; //更新最后的tick

            #endregion 生成or更新K线
        }

        /// <summary>
        /// 接收分钟测试数据
        /// </summary>
        /// <param name="min"></param>
        internal void OnUpdatePerMin(Bar min)
        {
            CurrentMinBar = min;

            DateTime dtBegin = DateTime.MaxValue;
            switch (IntervalType)
            {
                case IntervalType.Sec:
                    dtBegin = min.DateTime.Date.AddHours(min.DateTime.Hour).AddMinutes(min.DateTime.Minute).AddSeconds(min.DateTime.Second / Interval * Interval);
                    break;

                case IntervalType.Min:
                    dtBegin = min.DateTime.Date.AddHours(min.DateTime.Hour).AddMinutes(min.DateTime.Minute / Interval * Interval);
                    break;

                case IntervalType.Hour:
                    dtBegin = min.DateTime.Date.AddHours(min.DateTime.Hour / Interval * Interval);
                    break;

                case IntervalType.Day:
                    dtBegin = min.DateTime;
                    break;

                case IntervalType.Week:
                    //dtBegin = DateTime.ParseExact(min.TradingDay.ToString(), "yyyyMMdd", null);
                    dtBegin = dtBegin.Date.AddDays(1 - (byte)dtBegin.DayOfWeek);
                    break;

                case IntervalType.Month:
                    //dtBegin = DateTime.ParseExact(min.TradingDay.ToString(), "yyyyMMdd", null);
                    dtBegin = new DateTime(dtBegin.Year, dtBegin.Month, 1);
                    break;

                case IntervalType.Year:
                    //dtBegin = DateTime.ParseExact(min.TradingDay.ToString(), "yyyyMMdd", null);
                    dtBegin = new DateTime(dtBegin.Year, 1, 1);
                    break;

                default:
                    throw new Exception("参数错误");
            }
            if (Count == 0) //无数据
            {
                Bar bar = new Bar
                {
                    DateTime = dtBegin,
                    PreVol = min.Volume,
                    OpenInterest = min.OpenInterest,
                    Volume = min.Volume, // kOld.preVol == 0 ? 0 : _tick.Volume - kOld.preVol;
                };
                bar.High = min.High;
                bar.Low = min.Low;
                bar.Open = min.Open;
                bar.Close = min.Close;
                Add(bar);
            }
            else
            {
                Bar bar = this[CurrentBar];
                if (bar.DateTime == dtBegin) //在当前K线范围内
                {
                    bar.High = Math.Max(bar.High, min.High);
                    bar.Low = Math.Min(bar.Low, min.Low);
                    bar.Volume += min.Volume;
                    bar.OpenInterest = min.OpenInterest;
                    bar.Close = min.Close;

                    this[CurrentBar] = bar; //更新会与 _onChange?.Invoke(0, old, item); 连动
                }
                else if (dtBegin > bar.DateTime)
                {
                    Bar di = new Bar
                    {
                        DateTime = dtBegin,
                        Volume = min.Volume,
                        OpenInterest = min.OpenInterest,
                        Open = min.Open,
                        High = min.High,
                        Low = min.Low,
                        Close = min.Close,
                    };
                    Add(di);
                }
            }
        }

        public void LoadData(Bar[] bars)
        {
            foreach (var bar in bars)
            {
                OnUpdatePerMin(bar);
            }
        }

        /// <summary>
        /// 在指定索引处添加参数
        /// </summary>
        /// <param name="index"></param>
        /// <param name="item"></param>
        protected override void InsertItem(int index, Bar item)
        {
            base.InsertItem(index, item);
            //Date.Add(double.Parse(item.DateTime.ToString("yyyyMMdd")));
            //Time.Add(double.Parse(item.DateTime.ToString("0.HHmmss")));
            DateTimeUTC.Add(DateService.DateTime2UTC(item.DateTime));
            Open.Add(item.Open);
            High.Add(item.High);
            Low.Add(item.Low);
            Volume.Add(item.Volume);
            OpenInterest.Add(item.OpenInterest);
            Close.Add(item.Close); //最后一项更新:用于触发指标相关执行
            _onChange?.Invoke(1, item, item);
        }

        /// <summary>
        /// 设置指定索引处的参数
        /// </summary>
        /// <param name="index"></param>
        /// <param name="item"></param>
        protected override void SetItem(int index, Bar item)
        {
            base.SetItem(index, item);
            Bar old = this[index];
            High[CurrentBar - index] = item.High;
            Low[CurrentBar - index] = item.Low;
            Volume[CurrentBar - index] = item.Volume;
            OpenInterest[CurrentBar - index] = item.OpenInterest;
            Close[CurrentBar - index] = item.Close; //最后一项更新:用于触发指标相关执行

            _onChange?.Invoke(0, old, item);
        }

        /// <summary>
        /// 移除指定索引处的数据
        /// </summary>
        /// <param name="index"></param>
        protected override void RemoveItem(int index)
        {
            Bar old = this[index];
            if (DateTimeUTC.Count == Count)
            {
                DateTimeUTC.RemoveAt(index);
                //Time.RemoveAt(index);
                Open.RemoveAt(index);
                High.RemoveAt(index);
                Low.RemoveAt(index);
                OpenInterest.Remove(index);
                Close.RemoveAt(index);
                Volume.RemoveAt(index);
            }
            base.RemoveItem(index);
            _onChange?.Invoke(-1, old, old);
        }

        /// <summary>
        /// 清除所有数据
        /// </summary>
        protected override void ClearItems()
        {
            DateTimeUTC.Clear();
            //Time.Clear();
            Open.Clear();
            High.Clear();
            Low.Clear();
            OpenInterest.Clear();
            Close.Clear();
            Volume.Clear();
            base.ClearItems();
        }

        /// <summary>
        /// 报单操作
        /// </summary>
        [Description("报单操作列表"), Category("交易")]
        public List<OrderItem> Operations { get; private set; } = new List<OrderItem>();

        /// <summary>
        /// 开多仓：买开,开仓就是建新仓，和平仓相对应。多仓指看好后市，买入后等上涨。空仓指看淡后市，买入后等待下跌赚钱。
        /// </summary>
        /// <param name="pLots"> </param>
        /// <param name="pPrice"> </param>
        /// <param name="pRemark">注释</param>
        public void Buy(int pLots, double pPrice, string pRemark = "")
        {
            this.Order(Direction.Buy, Offset.Open, pLots, pPrice, pRemark);
        }

        /// <summary>
        /// 平空仓：卖平
        /// </summary>
        /// <param name="pLots"> </param>
        /// <param name="pPrice"> </param>
        /// <param name="pRemark">注释</param>
        public void Sell(int pLots, double pPrice, string pRemark = "")
        {
            this.Order(Direction.Sell, Offset.Close, pLots, pPrice, pRemark);
        }

        /// <summary>
        /// 开空仓：卖开
        /// </summary>
        /// <param name="pLots"> </param>
        /// <param name="pPrice"> </param>
        /// <param name="pRemark">注释</param>
        public void SellShort(int pLots, double pPrice, string pRemark = "")
        {
            this.Order(Direction.Sell, Offset.Open, pLots, pPrice, pRemark);
        }

        /// <summary>
        /// 平多仓：买平
        /// </summary>
        /// <param name="pLots"> </param>
        /// <param name="pPrice"> </param>
        /// <param name="pRemark">注释</param>
        public void BuyToCover(int pLots, double pPrice, string pRemark = "")
        {
            this.Order(Direction.Buy, Offset.Close, pLots, pPrice, pRemark);
        }

        /// <summary>
        /// 报单
        /// </summary>
        /// <param name="pDirector">买卖方向（买还是卖）</param>
        /// <param name="pOffset"> </param>
        /// <param name="pLots">交易数量</param>
        /// <param name="pPrice">交易价格</param>
        /// <param name="pRemark">注释</param>
        private void Order(Direction pDirector, Offset pOffset, int pLots, double pPrice, string pRemark)
        {
            OrderItem order;
            //pPrice = (int)(pPrice / PriceTick) * PriceTick;

            if (this.Operations.Count == 0)
            {
                order = new OrderItem
                {
                    DateTiem = this[CurrentBar].DateTime,
                    Dir = pDirector,
                    Price = pPrice,
                    Lots = pLots,
                    Remark = pRemark
                };
            }
            else
            {
                order = new OrderItem
                {
                    DateTiem = this[this.CurrentBar].DateTime,
                    Dir = pDirector,
                    Offset = pOffset,
                    Price = pPrice,
                    Lots = pLots,
                    Remark = pRemark,

                    IndexEntryLong = this.LastOrder.IndexEntryLong,
                    IndexExitLong = this.LastOrder.IndexExitLong,
                    IndexEntryShort = this.LastOrder.IndexEntryShort,
                    IndexExitShort = this.LastOrder.IndexExitShort,
                    IndexLastEntryLong = this.LastOrder.IndexLastEntryLong,
                    IndexLastEntryShort = this.LastOrder.IndexLastEntryShort,

                    AvgEntryPriceLong = this.LastOrder.AvgEntryPriceLong,
                    AvgEntryPriceShort = this.LastOrder.AvgEntryPriceShort,
                    PositionLong = this.LastOrder.PositionLong,
                    PositionShort = this.LastOrder.PositionShort,
                    //EntryTimeLong = this.LastOrder.EntryTimeLong,
                    EntryDateShort = this.LastOrder.EntryDateShort,
                    EntryPriceLong = this.LastOrder.EntryPriceLong,
                    EntryPriceShort = this.LastOrder.EntryPriceShort,
                    ExitDateLong = this.LastOrder.ExitDateLong,
                    ExitDateShort = this.LastOrder.ExitDateShort,
                    ExitPriceLong = this.LastOrder.ExitPriceLong,
                    ExitPriceShort = this.LastOrder.ExitPriceShort,
                    LastEntryDateLong = this.LastOrder.LastEntryDateLong,
                    LastEntryDateShort = this.LastOrder.LastEntryDateShort,
                    LastEntryPriceLong = this.LastOrder.LastEntryPriceLong,
                    LastEntryPriceShort = this.LastOrder.LastEntryPriceShort
                };
            }

            switch (string.Format("{0}{1}", pDirector, pOffset))
            {
                case "BuyOpen":
                    order.PositionLong += pLots;

                    order.AvgEntryPriceLong = (this.LastOrder.PositionLong * this.LastOrder.AvgEntryPriceLong + pLots * pPrice) / order.PositionLong;
                    if (this.LastOrder.PositionLong == 0)
                    {
                        order.IndexEntryLong = this.CurrentBar;
                        order.EntryDateLong = this.DateTimeUTC[0];
                        //order.EntryTimeLong = this.Time[0];
                        order.EntryPriceLong = pPrice;
                    }
                    order.IndexLastEntryLong = this.CurrentBar;
                    order.LastEntryDateLong = this.DateTimeUTC[0];
                    //order.LastEntryTimeLong = this.Time[0];
                    order.LastEntryPriceLong = pPrice;
                    break;

                case "SellOpen":
                    order.PositionShort += pLots;

                    order.AvgEntryPriceShort = (this.LastOrder.PositionShort * this.LastOrder.AvgEntryPriceShort + pLots * pPrice) / order.PositionShort;
                    if (this.LastOrder.PositionShort == 0)
                    {
                        order.IndexEntryShort = this.CurrentBar;
                        order.EntryDateShort = this.DateTimeUTC[0];
                        //order.EntryTimeShort = this.Time[0];
                        order.EntryPriceShort = pPrice;
                    }
                    order.IndexLastEntryShort = this.CurrentBar;
                    order.LastEntryDateShort = this.DateTimeUTC[0];
                    //order.LastEntryTimeShort = this.Time[0];
                    order.LastEntryPriceShort = pPrice;
                    break;

                case "BuyClose":
                    if ((pLots = Math.Min(this.PositionShort, pLots)) <= 0)
                        return;

                    order.PositionShort -= pLots;

                    order.IndexExitShort = this.CurrentBar;
                    order.ExitDateShort = this.DateTimeUTC[0];
                    //order.ExitTimeShort = this.Time[0];
                    order.ExitPriceShort = pPrice;
                    break;

                case "SellClose":
                    if ((pLots = Math.Min(this.PositionLong, pLots)) <= 0)
                        return;

                    order.PositionLong -= pLots;

                    order.IndexExitLong = this.CurrentBar;
                    order.ExitDateLong = this.DateTimeUTC[0];
                    //order.ExitTimeLong = this.Time[0];
                    order.ExitPriceLong = pPrice;
                    break;
            }
            //this.LastOrder = order;

            this.Operations.Add(order);

            _rtnOrder?.Invoke(order, this);
        }

        private RtnOrder _rtnOrder;

        /// <summary>
        ///
        /// </summary>
        internal event RtnOrder OnRtnOrder
        {
            add
            {
                _rtnOrder += value;
            }
            remove
            {
                _rtnOrder -= value;
            }
        }

        #region 策略状态信息

        /// <summary>
        /// 当前持仓手数:多
        /// </summary>
        [Description("当前持仓手数:多"), Category("状态"), ReadOnly(true), Browsable(false)]
        public int PositionLong { get => this.LastOrder.PositionLong; }

        /// <summary>
        /// 当前持仓手数:空
        /// </summary>
        [Description("当前持仓手数:空"), Category("状态"), ReadOnly(true), Browsable(false)]
        public int PositionShort { get => this.LastOrder.PositionShort; }

        /// <summary>
        /// 当前持仓手数:净
        /// </summary>
        [Description("当前持仓手数:净"), Category("状态"), ReadOnly(true), Browsable(false)]
        public int PositionNet { get => this.LastOrder.PositionLong - this.LastOrder.PositionShort; }

        /// <summary>
        /// 当前持仓首个建仓时间:多(yyyyMMdd.HHmmss)
        /// </summary>
        [Description("当前持仓首个建仓时间:多(yyyyMMdd.HHmmss)"), Category("状态"), ReadOnly(true), Browsable(false)]
        public double EntryDateLong { get => this.LastOrder.EntryDateLong; }

        /// <summary>
        /// 当前持仓首个建仓时间:空(yyyyMMdd.HHmmss)
        /// </summary>
        [Description("当前持仓首个建仓时间:空(yyyyMMdd.HHmmss)"), Category("状态"), ReadOnly(true), Browsable(false)]
        public double EntryDateShort { get => this.LastOrder.EntryDateShort; }

        /// </summary>
        /// 当前持仓最后建仓时间:多(yyyyMMdd.HHmmss)
        /// </summary>
        [Description("当前持仓最后建仓时间:多(yyyyMMdd.HHmmss)"), Category("状态"), ReadOnly(true), Browsable(false)]
        public double LastEntryDateLong { get => this.LastOrder.LastEntryDateLong; }

        /// <summary>
        /// 当前持仓最后建仓时间:空(yyyyMMdd.HHmmss)
        /// </summary>
        [Description("当前持仓最后建仓时间:空(yyyyMMdd.HHmmss)"), Category("状态"), ReadOnly(true), Browsable(false)]
        public double LastEntryDateShort { get => this.LastOrder.LastEntryDateShort; }

        /// <summary>
        /// 当前持仓首个建仓价格:多
        /// </summary>
        [Description("当前持仓首个建仓价格:多"), Category("状态"), ReadOnly(true), Browsable(false)]
        public double EntryPriceLong { get => this.LastOrder.EntryPriceLong; }

        /// <summary>
        /// 当前持仓首个建仓价格:空
        /// </summary>
        [Description("当前持仓首个建仓价格:空"), Category("状态"), ReadOnly(true), Browsable(false)]
        public double EntryPriceShort { get => this.LastOrder.EntryPriceShort; }

        /// <summary>
        /// 当前持仓最后建仓价格:多
        /// </summary>
        [Description("当前持仓最后建仓价格:多"), Category("状态"), ReadOnly(true), Browsable(false)]
        public double LastEntryPriceLong { get => this.LastOrder.LastEntryPriceLong; }

        /// <summary>
        /// 当前持仓最后建仓价格:空
        /// </summary>
        [Description("当前持仓最后建仓价格:空"), Category("状态"), ReadOnly(true), Browsable(false)]
        public double LastEntryPriceShort { get => this.LastOrder.LastEntryPriceShort; }

        /// 当前持仓平均建仓价格:多
        /// </summary>
        [Description("当前持仓平均建仓价格:多"), Category("状态"), ReadOnly(true), Browsable(false)]
        public double AvgEntryPriceLong { get => this.LastOrder.AvgEntryPriceLong; }

        /// <summary>
        /// 当前持仓平均建仓价格:空
        /// </summary>
        [Description("当前持仓平均建仓价格:空"), Category("状态"), ReadOnly(true), Browsable(false)]
        public double AvgEntryPriceShort { get => this.LastOrder.AvgEntryPriceShort; }

        /// <summary>
        /// 当前持仓首个建仓到当前位置的Bar数:多(从0开始计数)
        /// </summary>
        [Description("当前持仓首个建仓到当前位置的Bar数:多(从0开始计数)"), Category("状态"), ReadOnly(true), Browsable(false)]
        public int BarsSinceEntryLong { get => this.CurrentBar - this.LastOrder.IndexEntryLong; }

        /// <summary>
        /// 当前持仓首个建仓到当前位置的Bar数:空(从0开始计数)
        /// </summary>
        [Description("当前持仓首个建仓到当前位置的Bar数:空(从0开始计数)"), Category("状态"), ReadOnly(true), Browsable(false)]
        public int BarsSinceEntryShort { get => this.CurrentBar - this.LastOrder.IndexEntryShort; }

        /// <summary>
        /// 当前持仓的最后建仓到当前位置的Bar计数:多(从0开始计数)
        /// </summary>
        [Description("当前持仓的最后建仓到当前位置的Bar计数:多(从0开始计数)"), Category("状态"), ReadOnly(true), Browsable(false)]
        public int BarsSinceLastEntryLong { get => this.CurrentBar - this.LastOrder.IndexLastEntryLong; }

        /// <summary>
        /// 当前持仓的最后建仓到当前位置的Bar计数:空(从0开始计数)
        /// </summary>
        [Description("当前持仓的最后建仓到当前位置的Bar计数:空(从0开始计数)"), Category("状态"), ReadOnly(true), Browsable(false)]
        public int BarsSinceLastEntryShort { get => this.CurrentBar - this.LastOrder.IndexLastEntryShort; }

        /// <summary>
        /// 最近平仓位置到当前位置的Bar计数:多(从0开始计数)
        /// </summary>
        [Description("最近平仓位置到当前位置的Bar计数:多(从0开始计数)"), Category("状态"), ReadOnly(true), Browsable(false)]
        public int BarsSinceExitLong { get => this.CurrentBar - this.LastOrder.IndexExitLong; }

        /// <summary>
        /// 最近平仓位置到当前位置的Bar计数:空(从0开始计数)
        /// </summary>
        [Description("最近平仓位置到当前位置的Bar计数:空(从0开始计数)"), Category("状态"), ReadOnly(true), Browsable(false)]
        public int BarsSinceExitShort { get => this.CurrentBar - this.LastOrder.IndexExitShort; }

        /// 最近平仓时间:多(yyyyMMdd.HHmmss)
        /// </summary>
        [Description("平仓时间:多(yyyyMMdd.HHmmss)"), Category("状态"), ReadOnly(true), Browsable(false)]
        public double ExitDateLong { get => this.LastOrder.ExitDateLong; }

        ///<summary>
        ///	最近平仓时间:空(yyyyMMdd.HHmmss)
        ///</summary>
        [Description("平仓时间:空(yyyyMMdd.HHmmss)"), Category("状态"), ReadOnly(true), Browsable(false)]
        public double ExitDateShort { get => this.LastOrder.ExitDateShort; }

        /// <summary>
        /// 最近平仓价格:多
        /// </summary>
        [Description("平仓价格:多"), Category("状态"), ReadOnly(true), Browsable(false)]
        public double ExitPriceLong { get => this.LastOrder.ExitPriceLong; }

        /// <summary>
        /// 最近平仓价格:空
        /// </summary>
        [Description("平仓价格:空"), Category("状态"), ReadOnly(true), Browsable(false)]
        public double ExitPriceShort { get => this.LastOrder.ExitPriceShort; }

        /// <summary>
        /// 当前持仓浮动盈亏:多
        /// </summary>
        [Description("浮动盈亏:多"), Category("状态"), ReadOnly(true), Browsable(false)]
        public double PositionProfitLong { get => this.Count == 0 ? 0 : ((this.Close[0] - this.LastOrder.AvgEntryPriceLong) * this.LastOrder.PositionLong); }

        /// <summary>
        /// 当前持仓浮动盈亏:空
        /// </summary>
        [Description("浮动盈亏:空"), Category("状态"), ReadOnly(true), Browsable(false)]
        public double PositionProfitShort { get => this.Count == 0 ? 0 : ((this.LastOrder.AvgEntryPriceShort - this.Close[0]) * this.LastOrder.PositionShort); }

        /// <summary>
        /// 当前持仓浮动盈亏:净
        /// </summary>
        [Description("浮动盈亏:净"), Category("状态"), ReadOnly(true), Browsable(false)]
        public double PositionProfit { get => this.PositionProfitLong + this.PositionProfitShort; }

        #endregion 策略状态信息
    }
}